﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

using System.Windows.Forms;

using DotSpatial.Data;
//using NetTopologySuite;

using CSA.Lib;

namespace CSA.TSPProblem
{
    public class CSTSPProblem : ICSOptimizationProblem
    {
        #region Fields

        Hashtable m_Paras = new Hashtable();
        Hashtable m_CustomOPerators = new Hashtable();    //map<string,CSFuntionInfo* >
        CSAgorithmsHelper m_AgorithmsHelper = new CSAgorithmsHelper();
        Hashtable m_CitysInfo = new Hashtable();    //map<int,CityInfo>
        List<int> m_CityIDs = new List<int>();
        int m_AntibodyLength=0;
        double m_AffCoe;

        String m_CityFile = "";    
        String m_PathFile = "";

        TSPOutputConfigUI m_CustomOutputConfigUI = new TSPOutputConfigUI();
        TSPCustomEncodeUI m_CustomEncodeUI = new TSPCustomEncodeUI();
        TSPCustomEvaluationUI m_CustomEvaluationUI = new TSPCustomEvaluationUI();

        bool m_ProblemInitialized = false;

        #endregion

        public CSTSPProblem()
        {
            String nErrorStr = "";
            InitialProblem(ref nErrorStr);
        }

        ~CSTSPProblem()
        { }

        public bool InitialProblem(ref String nErrorStr)
        {       
            CSFuntionInfo nKOptOperator = new CSFuntionInfo();
            nKOptOperator.Caption = "K-Opt Operator";
            nKOptOperator.Name = "KOpt";
            CSParameter nOptPara = new CSParameter();
            nOptPara.Type = ParameterType.enumInt;
            nOptPara.Caption = "Iteration";
            nOptPara.Name = "Iteration";
            nOptPara.Description = "Iterations";
            int nDefaultValue = 1;
            nOptPara.Value = nDefaultValue;
            nOptPara.DomainType = enumParasDomain.enumDomainExtent;
            string low1 = "1";
            string up1 = "20";
            List<string> nDomains1 = new List<string>();
            nDomains1.Add(low1);
            nDomains1.Add(up1);
            nOptPara.DomainValues = nDomains1;
            Hashtable nParas = nKOptOperator.Parameters;
            nParas.Add("Iteration", nOptPara);
            m_CustomOPerators.Add("KOpt", nKOptOperator);
            return true;
        }

        public bool Initialization(ref String nErrorStr)
        {
            if (m_ProblemInitialized == true)
            {
                return true;
            }
            m_CityIDs.Clear();
            //Read file         
            String nFilePath = m_CityFile;
            //File exist
            bool Exist = System.IO.File.Exists(nFilePath);
            if (!Exist)
            {
                nErrorStr = "The datasource does not exist！";
                return false;
            }

            DotSpatial.Data.ShapefileDataProvider nShp = new DotSpatial.Data.ShapefileDataProvider();
            IFeatureSet pFeatureSet = nShp.Open(nFilePath);
            if (pFeatureSet.FeatureType != DotSpatial.Topology.FeatureType.Point)
            {
                nErrorStr = "You should select a point file！";
                return false;
            }
            for (int i = 0; i < pFeatureSet.Features.Count; i++)
            {
                DotSpatial.Topology.Point pBasicGeo = (DotSpatial.Topology.Point)pFeatureSet.Features[i].BasicGeometry;
                CityInfo nNewCityInfo = new CityInfo();
                nNewCityInfo.X = pBasicGeo.X;
                nNewCityInfo.Y = pBasicGeo.Y;
                m_CitysInfo.Add(pFeatureSet.Features[i].Fid, nNewCityInfo);
                m_CityIDs.Add(pFeatureSet.Features[i].Fid);
            }
            m_AntibodyLength = m_CityIDs.Count;
            m_ProblemInitialized = true;
            pFeatureSet.Close();
            return true;
        }

        public String CityFile
        {
            get { return m_CityFile; }
            set 
            { 
                m_CityFile = value;
                m_ProblemInitialized = false;  
            }
        }    

        public String TSPPathFile
        {
            get { return m_PathFile; }
            set { m_PathFile = value; }
        }

        public List<CSObjectiveInfo> Objectives
        {
            get 
            {
                List<CSObjectiveInfo> nObjectives = new List<CSObjectiveInfo>();
                CSObjectiveInfo nObjective = new CSObjectiveInfo();
                nObjective.Name = "RouteLength";
                nObjective.Caption = "Route Length";
                nObjective.IsCostObjecitve = true;
                nObjective.Description="The objective of the TSP is to find the shortest route.";
                nObjectives.Add( nObjective);
                return nObjectives;
            }
        }

        public string Name
        {
            get { return "TSP"; }
        }

        public string GeneDataType
        { get { return "Int32"; } }

        public string Caption { get { return global::CSA.TSPProblem.TextString.TSPProblemName; } }

        public string Description 
        {
            get 
            {
                return global::CSA.TSPProblem.TextString.TSPProblemDes ; 
            } 
        }
        public String ClassName { get { return "CSTSPProblem"; } }
        public String LibraryName { get { return "CSA.TSPProblem"; } }

        public Hashtable Inputs 
        {
            get 
            {
                Hashtable nInputs = new Hashtable();
                nInputs.Add("I_CityFile",m_CityFile);
                return nInputs; 
            } 
        }

        public Hashtable Outputs 
        { 
            get            
            {
                Hashtable nOutputs = new Hashtable();
                nOutputs.Add("O_PathFile", m_PathFile);
                return nOutputs;
            } 
        }

        public Hashtable CustomOperators
        {
            get { return m_CustomOPerators; }
        }

        public CSCustomUI GetCustomEncodeUI()
        {
            return m_CustomEncodeUI;
        }

        public CSCustomUI GetCustomObjectFuntionUI()
        {
            return m_CustomEvaluationUI;
        }

        public CSCustomUI GetCustomOutputConfigUI()
        {
            return m_CustomOutputConfigUI;
        }

        public CSAntibody CreateAb()
        {
            CSTSPAntibody nNewAb = new CSTSPAntibody();
            int nSize = m_CityIDs.Count;
            List<int> nCityIDs = new List<int>(m_CityIDs);
            for (int i = 0; i < nSize; ++i)
            {
                int nPos = m_AgorithmsHelper.NextRandomNumber(0, nCityIDs.Count - 1);
                int nValue = nCityIDs[nPos];
                nNewAb.AddGene(nValue);
                nCityIDs.Remove(nValue);
            }
            return nNewAb;
        }

        public double EvaluateAb(ref CSAntibody Antibody)
        {
            double nDis = 0.0;
            int nStartID = (int)Antibody.GetGene(0);
            Int32 nLength = (Int32)Antibody.AntibodyLength;
            int nEndID = (int)Antibody.GetGene(nLength - 1);
            int nFirstID = (int)Antibody.GetGene(0);
            int nCurID = (int)Antibody.GetGene(1);
            for (int nIt1 = 1; nIt1 < nLength; ++nIt1)
            {
                nCurID = (int)Antibody.GetGene(nIt1);
                CityInfo nCity1 = (CityInfo)m_CitysInfo[nCurID];
                CityInfo nCity2 = (CityInfo)m_CitysInfo[nFirstID];
                nDis += nCity1.CalDis(nCity2);
                nFirstID = nCurID;
            }
            CityInfo nLastCity1 = (CityInfo)m_CitysInfo[nEndID];
            CityInfo nLastCity2 = (CityInfo)m_CitysInfo[nStartID];
            nDis += nLastCity1.CalDis(nLastCity2);
            if (Antibody.ObjectiveValues.Count <1)
            {
                Antibody.ObjectiveValues.Add(nDis);
            }
            else
            {
                Antibody.ObjectiveValues[0] = nDis;
            }
            if (m_AffCoe == 0)
            {
                m_AffCoe = nDis * 1000;
            }
            double nAff = m_AffCoe / nDis;
            Antibody.Affinity = nAff;
            return nDis;
        }

        public bool MutateAb(ref CSAntibody Antibody, Double nMuatorRate)
        {
            CSTSPAntibody nNewAb = (CSTSPAntibody)Antibody;         
            Int32 nIt;
            Int32 nSize = (Int32)nNewAb.AntibodyLength;         
            for (nIt = 0; nIt < nSize; ++nIt)
            {
                Double nRnd = m_AgorithmsHelper.NextRandomNumber(0.0, 1.0);
                if (nRnd < nMuatorRate)
                {
                    int nPos = m_AgorithmsHelper.NextRandomNumber(0, nSize - 1);
                    while (nPos == nIt)
                    {
                        nPos = m_AgorithmsHelper.NextRandomNumber(0, nSize - 1);
                    }
                    int nTemp = (int)nNewAb.GetGene(nIt);
                    nNewAb.SetGene(nNewAb.GetGene(nPos), nIt);
                    nNewAb.SetGene(nTemp, nPos);
                }
            }       
            return true;
        }

        public bool CrossoverAb(ref CSAntibody Antibody1, ref CSAntibody Antibody2, List<bool> nCrossTemplate)
        {
            List<Int32> nAbGene1 = (List<Int32>)Antibody1.Genes;
            List<Int32> nAbGene2 = (List<Int32>)Antibody2.Genes;
            List<Int32> nGenes11 = new List<Int32>(nAbGene1);
            List<Int32> nGenes12 = new List<Int32>(nAbGene2);
            List<Int32> nGenes21 = new List<Int32>(nAbGene1);
            List<Int32> nGenes22 = new List<Int32>(nAbGene2);

            Int32 nSize = nGenes11.Count;
            Int32 nPos = m_AgorithmsHelper.NextRandomNumber(0, nSize - 1);
            List<Int32> nNewGenes1 = new List<Int32>();
            List<Int32> nNewGenes2 = new List<Int32>();

            int nNewCurCity10 = nGenes11[nPos];
            int nNewCurCity20 = nGenes11[nPos];

            nNewGenes1.Add(nNewCurCity10);
            nNewGenes2.Add(nNewCurCity20);


            Double nDisSum1 = 0.0;
            Double nDisSum2 = 0.0;

            for (int i = 0; i < nSize - 1; ++i)
            {

                int nNewCity11 = FindNextCity(nGenes11, nNewCurCity10, false);
                int nNewCity12 = FindNextCity(nGenes12, nNewCurCity10, false);

                nGenes11.Remove(nNewCurCity10);
                nGenes12.Remove(nNewCurCity10);

                CityInfo nNewCityInfo10, nNewCityInfo11, nNewCityInfo12;
                nNewCityInfo10 = (CityInfo)m_CitysInfo[nNewCurCity10];
                nNewCityInfo11 = (CityInfo)m_CitysInfo[nNewCity11];
                nNewCityInfo12 = (CityInfo)m_CitysInfo[nNewCity12];

                Double nDis11, nDis12;
                nDis11 = nNewCityInfo10.CalDis(nNewCityInfo11);
                nDis12 = nNewCityInfo10.CalDis(nNewCityInfo12);
                if (nDis11 < nDis12)
                {
                    nNewGenes1.Add(nNewCity11);
                    nNewCurCity10 = nNewCity11;
                    nDisSum1 += nDis11;
                }
                else
                {
                    nNewGenes1.Add(nNewCity12);
                    nNewCurCity10 = nNewCity12;
                    nDisSum1 += nDis12;
                }               

             
                int nNewCity21 = FindNextCity(nGenes21, nNewCurCity20, true);
                int nNewCity22 = FindNextCity(nGenes22, nNewCurCity20, true);
                nGenes21.Remove(nNewCurCity20);
                nGenes22.Remove(nNewCurCity20);
                CityInfo nNewCityInfo20, nNewCityInfo21, nNewCityInfo22;
                nNewCityInfo20 = (CityInfo)m_CitysInfo[nNewCurCity20];
                nNewCityInfo21 = (CityInfo)m_CitysInfo[nNewCity21];
                nNewCityInfo22 = (CityInfo)m_CitysInfo[nNewCity22];

                double nDis21, nDis22;
                nDis21 = nNewCityInfo20.CalDis(nNewCityInfo21);
                nDis22 = nNewCityInfo20.CalDis(nNewCityInfo22);

                if (nDis21 < nDis22)
                {
                    nNewGenes2.Add(nNewCity21);
                    nNewCurCity20 = nNewCity21;
                    nDisSum2 += nDis21;
                }
                else
                {
                    nNewGenes2.Add(nNewCity22);
                    nNewCurCity20 = nNewCity22;
                    nDisSum2 += nDis22;
                }
            }
            Antibody1.Genes = nNewGenes1;
            Antibody2.Genes = nNewGenes2;        
            return true;
        }

        int FindNextCity(List<Int32> nCityList, Int32 nCurCity, bool nInverse)
        {
            Int32 it = nCityList.FindIndex(delegate(Int32 CurCity) { return CurCity == nCurCity; });
            if (nInverse == false)
            {
                if (it + 1 < nCityList.Count)
                {
                    return nCityList[it + 1];
                }
                else
                {
                    return nCityList[0];
                }
            }
            else
            {
                if (it - 1 > -1)
                {
                    return nCityList[it - 1];
                }
                else
                {
                    return nCityList[nCityList.Count - 1];
                }
            }
        }

        public bool WriteOptimalResult(List<CSAntibody> BestAntibody)
        {
            if (BestAntibody != null && BestAntibody.Count > 0)
            {
                CheckResultFile();
                ShapefileDataProvider nSource = new ShapefileDataProvider();
                IFeatureSet pFeatureSet = nSource.Open(m_PathFile);
                List<int> nPathPoints = (List<int>)BestAntibody[0].Genes;
                if (nPathPoints != null)
                {
                    for (int i = 0; i < nPathPoints.Count; i++)
                    {
                        DotSpatial.Topology.Coordinate nFromPoint = new DotSpatial.Topology.Coordinate();
                        DotSpatial.Topology.Coordinate nToPoint = new DotSpatial.Topology.Coordinate();

                        CityInfo nFromCity = (CityInfo)m_CitysInfo[nPathPoints[i]];
                        CityInfo nToCity = null;
                        nFromPoint.X = nFromCity.X;
                        nFromPoint.Y = nFromCity.Y;
                        Int32 nFromID = nPathPoints[i];
                        Int32 nToID = nPathPoints[i];

                        if (i == nPathPoints.Count - 1)
                        {
                            nToCity = (CityInfo)m_CitysInfo[nPathPoints[0]];
                            nToID = nPathPoints[0];
                        }
                        else
                        {
                            nToCity = (CityInfo)m_CitysInfo[nPathPoints[i + 1]];

                            nToID = nPathPoints[i + 1];
                        }
                        nToPoint.X = nToCity.X;
                        nToPoint.Y = nToCity.Y;
                        List<DotSpatial.Topology.Coordinate> nPnts = new List<DotSpatial.Topology.Coordinate>();
                        nPnts.Add(nFromPoint);
                        nPnts.Add(nToPoint);
                        DotSpatial.Topology.LineString nLine = new DotSpatial.Topology.LineString(nPnts);

                        IFeature pFeature = pFeatureSet.AddFeature(nLine);
                        pFeature.DataRow["FromCity"] = nFromID;
                        pFeature.DataRow["ToCity"] = nToID;
                        if (i % 100 == 0)
                        {
                            pFeatureSet.Save();
                        }
                    }
                    pFeatureSet.Save();
                }
            }
            return true;
        }

        private void CheckResultFile()
        {
            try
            {
                DotSpatial.Data.IFeatureSet pFeatureSet = null;
                if (m_PathFile.Trim() != "" && System.IO.File.Exists(m_PathFile))
                {
                    ShapefileDataProvider nSource = new ShapefileDataProvider();
                    IFeatureSet pTempFetaureset = nSource.Open(m_PathFile);
                    bool nNeedDelete = false;
                    if (pTempFetaureset.FeatureType != DotSpatial.Topology.FeatureType.Line)
                    {
                        nNeedDelete = true;
                    }
                    else
                    {
                        if (!pTempFetaureset.DataTable.Columns.Contains("FromCity") ||
                            !pTempFetaureset.DataTable.Columns.Contains("ToCity"))
                        {
                            nNeedDelete = true;
                        }
                    }
                    if (nNeedDelete)
                    {
                        try
                        {
                            System.IO.File.Delete(m_PathFile);                
                            pFeatureSet = new FeatureSet(DotSpatial.Topology.FeatureType.Line);
                            pFeatureSet.DataTable.Columns.Add("FromCity", typeof(string));
                            pFeatureSet.DataTable.Columns.Add("ToCity", typeof(string));
                            pFeatureSet.SaveAs(m_PathFile, true);
                        }
                        catch
                        {
                            return;
                        }
                    }
                    else
                    {
                        pFeatureSet = pTempFetaureset;
                        pFeatureSet.IndexMode = false;
                        for (int i = pFeatureSet.Features.Count - 1; i > -1; --i)
                        {
                            IFeature pCurFeature = pFeatureSet.GetFeature(i);
                            pFeatureSet.Features.Remove(pCurFeature);
                        }
                        pFeatureSet.DataTable.Clear();
                        pFeatureSet.DataTable.AcceptChanges();
                        pFeatureSet.Save();
                    }
                }
                else
                {
                    pFeatureSet = new FeatureSet(DotSpatial.Topology.FeatureType.Line);
                    pFeatureSet.DataTable.Columns.Add("FromCity", typeof(string));
                    pFeatureSet.DataTable.Columns.Add("ToCity", typeof(string));
                    pFeatureSet.SaveAs(m_PathFile, true);
                }
            }
            catch
            { }
        }

        public bool CustomOperator(ref CSAntibody Antibody, String nOperatorName)
        {
            if (m_CustomOPerators.Contains(nOperatorName))
            {
                if (nOperatorName == "KOpt")
                {
                    OptAb(ref Antibody);
                }
            }
            return true;
        }

        public bool OptAb(ref CSAntibody Antibody)
        {
            return true;
        }

        public int AntibodyLength
        {
            get {return m_AntibodyLength;}          
        }

        public bool ReadEncodeParameters(XmlElement nEncodeNode, ref String nErrorInfo)
        {
            try
            {
                XmlElement nFileNode = (XmlElement)nEncodeNode.SelectSingleNode("./CityFile");
                m_CityFile = nFileNode.GetAttribute("FileName");         
            }
            catch
            {
                nErrorInfo = "Encode information have not setted corrected";
                return false;
            }
            return true;
        }

        public bool WriteEncodeParameters(XmlElement nEncodeNode, ref String nErrorInfo)
        {
            try
            {
                nEncodeNode.RemoveAll();
                XmlNode nFileNode = nEncodeNode.OwnerDocument.CreateElement("CityFile");
                nEncodeNode.AppendChild(nFileNode);
                XmlAttribute nFileNameAttribute = nEncodeNode.OwnerDocument.CreateAttribute("FileName");
                nFileNameAttribute.Value = m_CityFile;
                nFileNode.Attributes.Append(nFileNameAttribute);              
                return true;
            }
            catch(Exception ex)
            {
                nErrorInfo = "Wrtting encode information encounter an error:"+ex.Message;
                return false;
            }
        }

        public bool ValidateConfigure(ref String nErrorStr)
        {
            try
            {  
                if (m_CityFile == "")
                {                  
                    nErrorStr="please set the city file.";
                    return false;
                }             
                if (m_PathFile == "")
                {
                    nErrorStr="please set the file used to saving the tour.";
                    return false;
                }   
                return true;
            }
            catch(Exception ex)
            {
                MessageBox.Show(ex.Message);
                return false;
            }           
        }

        public bool ReadEvaluationParameters(XmlElement nEvaluationNode, ref String nErrorInfo)
        {
            return true;
        }

        public bool WriteEvaluationParameters(XmlElement nEvaluationNode, ref String nErrorInfo)
        {
            return true;
        }

        public bool ReadOutputParameters(XmlElement nOutputNode, ref String nErrorInfo)
        {
            try
            {
                XmlElement nFileNode = (XmlElement)nOutputNode.SelectSingleNode("./PathFile");
                m_PathFile = nFileNode.GetAttribute("FileName");              
            }
            catch
            {
                nErrorInfo = "Reading the encode information encounter an error。";
                return false;
            }
            return true;
        }

        public bool WriteOutputParameters(XmlElement nOutputNode, ref String nErrorInfo)
        {
            try
            {
                nOutputNode.RemoveAll();
                XmlNode nFileNode = nOutputNode.OwnerDocument.CreateElement("PathFile");
                nOutputNode.AppendChild(nFileNode);
                XmlAttribute nFileNameAttribute = nOutputNode.OwnerDocument.CreateAttribute("FileName");
                nFileNameAttribute.Value = m_PathFile;
                nFileNode.Attributes.Append(nFileNameAttribute);             
                return true;
            }
            catch (Exception ex)
            {
                nErrorInfo = "Writting the output settings encounter an error :" + ex.Message;
                return false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public CSMOProcessor MultiObjectiveProcessor
        {
            get { return null; }
            set { ;}
        }
    }

    class CityInfo
    {
        #region Fields
        //int ID;
        public double X;
        public double Y;
        #endregion

        public CityInfo() 
        {
            //ID = 0;
            X = 0;
            Y = 0;
        }
        ~CityInfo() 
        { }
        public double CalDis(CityInfo nCity)
        {
            double Dis = 0;
            Dis = Math.Sqrt((nCity.X - X) * (nCity.X - X) + (nCity.Y - Y) * (nCity.Y - Y));
            return Dis;
        }
    }
}
